#include <tinx/stdio.h>
#include <tinx/string.h>
#include <tinx/stdlib.h>

static char cmdline[128];
static char *args[64];

int print_error(int err)
{
    if (err < 0)
    {
        printf("%s\n", strerror(err));
    }
    return err;
}

void builtin_exit(int argc, char *argv[])
{
    if (argc == 1)
        return exit(0);
    exit(atoi(argv[1]));
}

int builtin_cd(int argc, char *argv[])
{
    if (argc == 1)
        return chdir("/");
    return print_error(chdir(argv[1]));
}

int builtin_mkdir(int argc, char *argv[])
{
    if (argc != 2)
        return -EINVAL;
    return print_error(mkdir(argv[1], 0755));
}

int builtin_rmdir(int argc, char *argv[])
{
    if (argc != 2)
        return -EINVAL;
    return print_error(rmdir(argv[1]));
}

int builtin_symlink(int argc, char *argv[])
{
    if (argc != 3)
        return -EINVAL;
    return print_error(symlink(argv[1], argv[2]));
}

int builtin_rm(int argc, char *argv[])
{
    if (argc != 2)
        return -EINVAL;
    return print_error(unlink(argv[1]));
}

int builtin_reboot(int argc, char *argv[])
{
    return reboot();
}

int builtin_shutdown(int argc, char *argv[])
{
    return shutdown();
}

int dupfile(int argc, char *argv[], fd_t dupfd[3])
{
    char *outfile = NULL;

    for (int i = 1; i < argc; i++)
    {
        if (!strcmp(argv[i], ">"))
        {
            outfile = argv[i + 1];
            argv[i] = NULL;
            i++; // 跳过后面的文件
            continue;
        }
    }

    for (size_t i = 0; i < 3; i++)
    {
        dupfd[i] = EOF;
    }

    if (outfile)
    {
        fd_t outfd = open(outfile, O_CREAT | O_WRONLY, 0755);
        if (outfd == EOF)
        {
            printf("open file %s failure\n", outfile);
            goto rollback;
        }
        dupfd[1] = outfd;
    }
    return 0;

rollback:
    for (size_t i = 0; i < 3; i++)
    {
        if (dupfd[i] != EOF)
        {
            close(dupfd[i]);
        }
    }
    return EOF;
}

int builtin_command(int argc, char *argv[])
{
    char filename[NAME_LEN];
    sprintf(filename, "/bin/%s", argv[0]);

    stat_t statbuf;
    if (stat(filename, &statbuf) < 0)
    {
        printf("%s: command not found!\n", argv[0]);
        return -ENOENT;
    }

    pid_t pid = fork();
    if (pid)
    {
        int status;
        waitpid(pid, &status, 0);
        return status;
    }

    fd_t dupfd[3];
    if (dupfile(argc, argv, dupfd) == EOF)
    {
        exit(EOF);
    }

    if (dupfd[1] != EOF)
    {
        dup2(dupfd[1], STDOUT_FILENO);
    }

    int ret = execve(filename, argv, NULL);
    exit(ret);

}

void print_prompt()
{
    printf("# ");
    return;

    stat_t statbuf;
    stat("./", &statbuf);

    int nr = statbuf.nr;
    int dev = statbuf.dev;

    chdir("../");
    fd_t fd = open(".", O_RDONLY, 0);
    fstat(fd, &statbuf);

    dentry_t entry;

    while (1)
    {
        int ret = readdir(fd, &entry, 1);
        if (ret <= 0)
            break;
        if (entry.nr == nr && statbuf.dev == dev)
            break;
        if (statbuf.dev != dev)
        {
            fd_t efd = open(entry.name, O_RDONLY, 0);
            stat_t estat;
            fstat(efd, &estat);
            close(efd);
            if (estat.dev == dev && estat.nr == nr)
                break;
        }
    }

    close(fd);
    chdir(entry.name);

    if (!strcmp(entry.name, ".") || !strcmp(entry.name, ".."))
    {
        entry.name[0] = '/';
        entry.name[1] = EOS;
    }

    printf("[root %s]# ", entry.name);
}

int execute(int argc, char *argv[])
{
    char *cmd = argv[0];
    if(!strcmp(cmd, "cd"))
    {
        return builtin_cd(argc, argv);
    }
    else if(!strcmp(cmd, "exit"))
    {
        builtin_exit(argc, argv);
    }
    else if(!strcmp(cmd, "reboot"))
    {
        return builtin_reboot(argc, argv);
    }
    else if(!strcmp(cmd, "shutdown"))
    {
        return builtin_shutdown(argc, argv);
    }
    else if(!strcmp(cmd, "mkdir"))
    {
        return builtin_mkdir(argc, argv);
    }
    else if(!strcmp(cmd, "rmdir"))
    {
        return builtin_rmdir(argc, argv);
    }
    else if(!strcmp(cmd, "symlink"))
    {
        return builtin_symlink(argc, argv);
    }
    else if (!strcmp(cmd, "rm"))
    {
        return builtin_rm(argc, argv);
    }
    return builtin_command(argc, argv);
}

int readline(char *cmd, u32 max_chars)
{
    int nr = 0;

    while (nr < max_chars)
    {
        char c;
        read(STDIN_FILENO, &c, 1);
        switch (c)
        {
        case '\n':
            write(STDOUT_FILENO, &c, 1);
            return nr;
        case '\b':
            if (!nr)
                break;
            write(STDOUT_FILENO, &c, 1);
            nr--;
            cmd[nr] = EOS;
            break;
        default:
            write(STDOUT_FILENO, &c, 1);
            cmd[nr++] = c;
        }
    }
    return nr;
}

// 解析命令
int cmd_parse(char *line, char *argv[])
{
    int argc = 0;

    // 判断当前字符是不是 token
    bool token = true;

    char *ptr = line;

    while (1)
    {
        if (!*ptr)
        {
            break;
        }
        if (*ptr == ' ')
        {
            *ptr++ = 0;
            token = true;
            continue;
        }
        if (token)
        {
            argv[argc] = ptr;
            token = false;
            argc++;
        }
        ptr++;
    }
    return argc;
}

int main()
{
    int cmdlen = sizeof(cmdline);

    while (1)
    {
        memset(cmdline, 0, cmdlen);
        memset(args, 0, sizeof(args));
        print_prompt();
        cmdlen = readline(cmdline, sizeof(cmdline));
        if (!cmdlen)
        {
            continue;
        }
        int argc = cmd_parse(cmdline, args);
        int ret = execute(argc, args);
    }
}